import inspect
import os
import re
from typing import Union

from PIL.ImageChops import screen

from selenium.webdriver.common.by import By
import numpy as np
from appium import webdriver
from appium.webdriver import WebElement
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from selenium.common import StaleElementReferenceException
import cv2

from common.UiSelector import UiSelector

# from skimage.metrics import structural_similarity as ssim

LocatorSpec = Union[tuple[By, str] or tuple[UiSelector, str] or list[tuple[UiSelector, str]]]
# 获取UiSelector所有属性
UiSelector_attr = [getattr(UiSelector, attr) for attr in dir(UiSelector) if
                   not callable(getattr(UiSelector, attr)) and not attr.startswith('__')]


class PageBase:
    screenshot_path = '../screenshots'
    image_path = '../image'

    def __init__(self, driver: webdriver.Remote):
        self.driver = driver

        if not os.path.exists(self.screenshot_path):
            os.mkdir(self.screenshot_path)
        if not os.path.exists(self.image_path):
            os.mkdir(self.image_path)

    def locator_by_uiautomator(self, loc: LocatorSpec, parent_number: int = 0,
                               element: WebElement = None) -> WebElement:
        code = ''
        end = ''
        if isinstance(loc, tuple):
            return self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'{UiSelector.NEW}{loc[0]}("{loc[1]}")')
        for index, elem in enumerate(loc):
            if index < parent_number:
                code += f'{elem[0]}("{elem[1]}"){UiSelector.CHILD}({UiSelector.NEW}'
                end += ')'
            else:
                code += f'{elem[0]}("{elem[1]}")'
        if element:
            return element.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'{UiSelector.NEW}{code}{end}')
        return self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'{UiSelector.NEW}{code}{end}')

    def locator(self, loc: LocatorSpec, parent_number=0, element: WebElement = None) -> WebElement:

        if loc[0] in UiSelector_attr or loc[0][0] in UiSelector_attr:
            return self.locator_by_uiautomator(loc, parent_number, element)
        return self.driver.find_element(*loc)

    def click(self, loc: LocatorSpec, parent_number=0):
        return self.locator(loc, parent_number).click()

    def input(self, loc: LocatorSpec, value, parent_number=0):
        return self.locator(loc, parent_number).send_keys(value)

    def find_text(self, text, loc: LocatorSpec = None, parent_number=0):
        text_loc = (UiSelector.TEXT_CONTAINS, text)
        if not loc:
            return self.locator(text_loc, parent_number)
        if isinstance(loc, list):
            loc.append(text_loc)
            return self.locator(loc, parent_number)
        return self.locator([loc, text_loc], parent_number)

    def click_text(self, text, loc: LocatorSpec = None, parent_number=0):
        self.find_text(text, loc, parent_number).click()

    def flick(self, x, y, dx, dy):
        return self.driver.flick(x, y, dx, dy)

    def locators(self, loc: tuple[By, str]):
        return self.driver.find_elements(*loc)

    def loop_click(self, loc: tuple[By, str], times):
        locators = self.locators(loc)
        if locators is []:
            print("无可选对象")
            return
        for i in range(times):
            if i < len(locators):
                try:
                    locators[i].click()
                except StaleElementReferenceException:
                    locators = self.locators(loc)
                    locators[i].click()
                    continue

    def cv(self, img1, img2):
        # # 算法求出两张图片差异值similarity_index
        # similarity_index, diff = ssim(img1, img2, multichannel=True, full=True)
        # # 将差异图归一化到 0-255 范围，以便于保存
        # diff = (diff * 255).astype("uint8")
        # # 保存差异图
        # cv2.imwrite('./screenshots/diff_image.png', diff)

        # 另一个算法求两张图片差异值err
        err = np.sum((img1.astype("float") - img2.astype("float")) ** 2)
        err /= float(img1.shape[0] * img2.shape[1])
        # print(similarity_index, err)
        print(f'两图差异值{err}')
        return err

    def current_cv(self, loc: LocatorSpec, func, parent_number=0):

        self.locator(loc, parent_number).screenshot(f'{self.screenshot_path}/{loc[1]}_1.png')
        img1 = cv2.imread(f'{self.screenshot_path}/{loc[1]}_1.png')
        func()
        self.locator(loc, parent_number).screenshot(f'{self.screenshot_path}/{loc[1]}_2.png')
        img2 = cv2.imread(f'{self.screenshot_path}/{loc[1]}_2.png')
        self.cv(img1, img2)

    def version_cv(self, loc: LocatorSpec, parent_number=0):

        if not os.path.exists(f'{self.screenshot_path}/{loc[1]}.png'):
            print("无上个测试图片，将获取本次测试图片作为基准图")
            self.locator(loc, parent_number).screenshot(f'{self.screenshot_path}/{loc[1]}.png')
            return
        img1 = cv2.imread(f'{self.screenshot_path}/{loc[1]}.png')
        self.locator(loc, parent_number).screenshot(f'{self.screenshot_path}/{loc[1]}.png')
        img2 = cv2.imread(f'{self.screenshot_path}/{loc[1]}.png')
        self.cv(img1, img2)

    def scroll(self, loc1: LocatorSpec, loc2: LocatorSpec):
        self.driver.scroll(self.locator(loc1), self.locator(loc2))

    def get_image_match_xy(self, template_img):
        threshold = 0.8
        # 读取原始图像和目标图像
        if not os.path.exists(template_img):
            raise "图片不存在"
        template = cv2.imread(template_img)
        main_img_path = f'{self.screenshot_path}/screenshots.jpg'
        self.driver.get_screenshot_as_file(main_img_path)
        main_img = cv2.imread(main_img_path)
        # os.remove(main_img_path)

        # 使用模板匹配函数进行图像匹配
        result = cv2.matchTemplate(main_img, template, cv2.TM_CCOEFF_NORMED)
        # 在原始图像中绘制匹配到的目标图像位置
        h, w = template.shape[:2]

        loc = np.where(result >= threshold)
        if len(loc[0]) == 0:
            return

        for pt in zip(*loc[::-1]):
            cv2.rectangle(main_img, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2)

        # 获取匹配结果中的最大值和最小值的位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

        # 中心坐标
        center = tuple(map(int, (max_loc[0] + w / 2, max_loc[1] + h / 2)))

        # 显示原始图像和匹配结果
        new_width = int(main_img.shape[1] * 0.5)
        new_height = int(main_img.shape[0] * 0.5)


        cv2.namedWindow('Matched Image', cv2.WINDOW_NORMAL)
        cv2.resizeWindow('Matched Image', new_width, new_height)
        cv2.imshow('Matched Image', main_img)
        cv2.waitKey(0)
        return center

    def click_match_xy(self, img):
        # 获取当前调用栈
        stack = inspect.stack()
        # 打印调用者的信息
        caller_frame = stack[1]
        caller_name = caller_frame.function
        match_xy = self.get_image_match_xy(img)
        if not match_xy:
            print(f'{caller_name}{img}未匹配到结果')
            return
        x, y = match_xy
        print(match_xy)
        print(type(match_xy))
        self.driver.tap([(x, y)])
        return True
